#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2019 crane <crane@crane-pc>
#
# Distributed under terms of the MIT license.

"""

"""

# ATTENTION, NOTE, BUG, 这里不能用map, 因为遍历时不是按顺序遍历的
# byte_map = {
#     0x7F:     1,        # 小于0x7f需要1个字节
#     0x7FF:    2,        # 小于0x7ff需要2个字节
#     0xFFFF:   3,        # 小于0xffff需要3个字节
#     0x10FFFF: 4,        # 小于0x10FFFF需要4个字节
# }

byte_map = [
            (0x7F,     1),        # 小于0x7f需要1个字节
            (0x7FF,    2),        # 小于0x7ff需要2个字节
            (0xFFFF,   3),        # 小于0xffff需要3个字节
            (0x10FFFF, 4),        # 小于0x10FFFF需要4个字节
]

prefix_map = {
    1 : "0",
    2 : "110",
    3 : "1110",
    4 : "11110",
}

byte_len_map = {
    "0"         : 1,
    "110"       : 2,
    "1110"      : 3,
    "11110"     : 4,
}


def pure_bin(num):
    return bin(num)[2:]        # 去掉"0b"前缀

# 返回uincode_num 需要的utf8编码字节
# 1:第一步, 计算需要utf8字节
def unicode_byte_len(unicode_num):
    for byte_limit, byte_len in byte_map:
        if unicode_num <= byte_limit:
            # print(hex(unicode_num), hex(byte_limit))
            return byte_len

    # 超过utf8所能接受的最大范围
    raise Exception("invalid unicode_num")
    return -1

# 2: 第二步: 计算utf8添加prefix之前字符
def unicode_origin_bin(unicode_num, byte_len):
    after_padden_len = after_padded_length(byte_len)

    origin_binary = bin(unicode_num)[2:]        # 去掉0b
    # origin_binary = pure_bin(unicode_num)
    origin_len = len(origin_binary)

    need = after_padden_len - origin_len

    return need * "0" + origin_binary

def after_padded_length(byte_len):
    prefix = prefix_map[byte_len]
    prefix_len = len(prefix)

    # 输入4个字节, 4*8 - prefix_map[4] - 3 * "10"
    return (byte_len * 8) - prefix_len - (byte_len - 1) * len("10")

# 3: 添加prefix
def pad_prefix(origin_binary, byte_len):
    prefix = prefix_map[byte_len]
    result = prefix + origin_binary

    for i in range(1, byte_len):
        result = pad_10(result, i)

    return result

def pad_10(binary, byte):
    pos = byte * 8
    return binary[0:pos] + "10" + binary[pos:]

# HINT: 对外接口: 把unicode数字(code point)编码成utf8格式
def utf8_encode(unicode_num) :
    # 返回二进制, 10进制, 16进制
    print("\n")
    byte_len = unicode_byte_len(unicode_num)
    print("byte_len [%s]" % byte_len)
    origin_binary = unicode_origin_bin(unicode_num, byte_len)        # 在没有添加utf-8之前
    print("[%s %s] origin unicode binary [%s]" % (hex(unicode_num), unicode_num, origin_binary))

    utf8_binary = pad_prefix(origin_binary, byte_len)
    print("utf8 binary [%s]" % utf8_binary)

    # 转换为10进制
    decimal = int(utf8_binary, 2)

    return bin(decimal), str(decimal), hex(decimal)

# =================== decode =====================
def utf8_byte_len(by):
    # 输入一个byte/int <= 255
    leading = pure_bin(by)
    for prefix, byte_len in byte_len_map.items():
        if leading.startswith(prefix):
            return byte_len

    raise "invlaid utf8 binary, invalid leading"     # 不合法的utf8

def utf8_origin_bin(bytes, byte_len):
    leading_bin = pure_bin(bytes[0])
    prefix_len = len(prefix_map[byte_len])
    leading_origin_bin = leading_bin[prefix_len:]

    result_bin = leading_origin_bin


    # for i in range(1, byte_len):
    # ATTENTION: 如果"bytes"长度和utf8传入长度匹配, 可以直接遍历bytes[:1].
    # ATTENTION: 如果bytes长度可能大于utf8传入长度, 使用byte_len作为索引上限遍历!
    for by in bytes[1:]:
        # by = bytes[i]
        result_bin += depad_10(by)

    return result_bin

def depad_10(by):
    # pad_10的反操作, 去掉byte前面的"10"
    following_bin = pure_bin(by)
    if not following_bin.startswith("10"):
        raise Exception("invlaid utf8 binary, invalid following, following must start with '10'")     # 不合法的utf8

    return following_bin[2:]

def utf8_decode(bytes) :
    # 返回二进制, 10进制, 16进制
    byte_len = utf8_byte_len(bytes[0])
    if len(bytes) != byte_len:
        raise Exception("invlaid utf8 binary, invalid length, need length %s"% byte_len)     # 不合法的utf8
    origin_bin = utf8_origin_bin(bytes, byte_len)

    decimal = int(origin_bin, 2)
    return origin_bin, decimal, hex(decimal)


def main():
    # NOTE1: 试验1: 借助unicode 命令行测试结果
    # 1: unicode 严
    # 2: unicode -x 4e25

    # NOTE2: 试验2: 使用hexedit编辑文件, 写入16进制, 保存后, 使用vim查看是否显示utf8响应字符
    #        结果, 有些特殊字符无法显示, 汉字均可显示

    # NOTE3: 试验3: 使用python ord(str_symbol), 获取字符的unicode
    #        结果, 有些特殊字符无法显示, 汉字均可显示
    #        ord(接受 code point)
    # NOTE4: 试验4: 使用python chr(unicode), 显示unicode的字符, 注意传入的是unicode
    #        结果, 有些特殊字符无法显示, 汉字均可显示

    # NOTE5: 试验5: python3: string.encode('utf-8'), 产生utf-8编码的bytes
    #        bytes.decode('utf-8') 使用utf-8解码, 返回字符串

    # NOTE6: 试验6: 中文unicode范围
    # 汉字：[0x4e00,0x9fa5]（或十进制[19968,40869]）
    #        chr(0x4e00)
    # 判断是否为汉字 0x4e00 <= ord(symbol) <= 0x9fa5

    # NOTE: CJK: chinese, japan, korea

    # NOTE: 常用符号范围https://www.cnblogs.com/cbscan/articles/4123310.html

    print("start main")

    # 王
    result = utf8_encode(0x738b)
    print(result)

    # 严的unicode "4E25", utf-8:"E4B8A5"
    # result = utf8_encode(0x4E25)
    result = utf8_encode(ord('严'))
    print(result)

    # Ԡ
    # result = utf8_encode(0x520)
    result = utf8_encode(0x530)
    print(result)
    # print(0x520 < 0x7ff)

    result = utf8_encode(0b10101001)
    print(result)

    print(after_padded_length(1))
    print(after_padded_length(2))
    print(after_padded_length(3))
    print(after_padded_length(4))

    # =================== decode =====================
    # 王 utf8 0xe78e8b
    bs =  [0xe7,  0x8e, 0x8b]
    result = utf8_decode(bs)
    print(result)

    bs =  "严".encode('utf8')
    result = utf8_decode(bs)
    print(result)

    bs =  "樊".encode('utf8')
    result = utf8_decode(bs)
    print(result)

    # bs =  [14, 12]
    # result = utf8_decode(bs)
    # print(result)

if __name__ == "__main__":
    main()
